
#include "DeviceManager.h"
#include "D3d11Utils.h"

DeviceManager::DeviceManager()
{
}

// Static
DeviceManager* DeviceManager::CreateDevice(int width, int height, bool fullscreen, HWND handle)
{
	HRESULT hr=0;

	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory( &sd, sizeof( sd ) );
	sd.BufferCount = 1;
	sd.BufferDesc.Width = width;
	sd.BufferDesc.Height = height;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = handle;
	sd.SampleDesc.Count = 1;
	sd.SampleDesc.Quality = 0;
	sd.Windowed = TRUE;


	DeviceManager* pDevMan = new DeviceManager();
	pDevMan->mIsFullscreen = fullscreen;
	pDevMan->mWidth = width;
	pDevMan->mHeight = height;

	D3D_FEATURE_LEVEL  featureLevelsRequested[3]={D3D_FEATURE_LEVEL_11_0,D3D_FEATURE_LEVEL_10_1,D3D_FEATURE_LEVEL_10_0};
	UINT               numLevelsRequested = 3;
	D3D_FEATURE_LEVEL  featureLevelsSupported[6];

	HR( D3D11CreateDeviceAndSwapChain( NULL, 
					D3D_DRIVER_TYPE_HARDWARE, 
					NULL, 
					0,
					featureLevelsRequested, 
					numLevelsRequested, 
					D3D11_SDK_VERSION, 
					&sd, 
					&pDevMan->mpSwapChain, 
					&pDevMan->mpDevice, 
					featureLevelsSupported,
					&pDevMan->mpContext)
	);

	pDevMan->CreateTargetView();
	pDevMan->CreateDepthStencilView(width,height);

	pDevMan->mpContext->OMSetRenderTargets( 1, &pDevMan->mpRenderTargetView, pDevMan->mpRenderTargetDepth);

	pDevMan->SetViewport(width,height);


	D3D11_DEPTH_STENCIL_DESC descD = D3D11_DEPTH_STENCIL_DESC();
	descD.DepthEnable = true;
	descD.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	descD.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;

	D3D11_RASTERIZER_DESC rastD = D3D11_RASTERIZER_DESC();
	rastD.CullMode = D3D11_CULL_BACK;
	rastD.FillMode = D3D11_FILL_SOLID;
		
	D3D11_SAMPLER_DESC sampD=D3D11_SAMPLER_DESC();
	sampD.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	sampD.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	sampD.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	sampD.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
		
    D3D11_BLEND_DESC blenD = D3D11_BLEND_DESC();
    blenD.IndependentBlendEnable = false;
    blenD.RenderTarget[0].BlendEnable =  false;
    blenD.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	

	pDevMan->mpDevice->CreateRasterizerState(&rastD,&pDevMan->mpRasterState);
	pDevMan->mpDevice->CreateDepthStencilState(&descD,&pDevMan->mpDepthState);
	pDevMan->mpDevice->CreateSamplerState(&sampD,&pDevMan->mpSamplerState);
    pDevMan->mpDevice->CreateBlendState(&blenD, &pDevMan->mpBlendState );


	pDevMan->mpContext->RSSetState(pDevMan->mpRasterState);
	pDevMan->mpContext->OMSetDepthStencilState(pDevMan->mpDepthState,0);
	pDevMan->mpContext->OMSetBlendState( pDevMan->mpBlendState, 0, 0xffffffff );

	ID3D11SamplerState* samps[1] = {pDevMan->mpSamplerState};
	pDevMan->mpContext->PSSetSamplers(0, 1, samps);
	pDevMan->mpContext->DSSetSamplers(0, 1, samps);
	
	return pDevMan;
}


// Public

bool DeviceManager::Resize(int width,int height)
{
	if(mIsFullscreen)return true;

	this->mWidth=width;
	this->mHeight=height;

	mpRenderTargetView->Release();
	mpRenderTargetDepth->Release();

	HR(mpSwapChain->ResizeBuffers(1, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 2));
	CreateTargetView();
	CreateDepthStencilView(width,height);
	SetViewport(width,height);

	mpContext->OMSetRenderTargets( 1, &mpRenderTargetView, mpRenderTargetDepth);

	return true;
}

// Public

void DeviceManager::SetViewport(int width, int height)
{
    D3D11_VIEWPORT vp;
    vp.Width = (float)width;
    vp.Height = (float)height;
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    vp.TopLeftX = 0;
    vp.TopLeftY = 0;
    
	mpContext->RSSetViewports( 1, &vp );

}

// Public

void DeviceManager::RestoreTarget()
{
    mpContext->OMSetRenderTargets( 1, &mpRenderTargetView, mpRenderTargetDepth);
	SetViewport(mWidth, mHeight);
}

// Public

void DeviceManager::Clear(float r, float g, float b, float a)
{
	float ClearColor[4] = { r, g, b, a};
	mpContext->ClearRenderTargetView(mpRenderTargetView, ClearColor);
	mpContext->ClearDepthStencilView(mpRenderTargetDepth, D3D11_CLEAR_DEPTH, 1, 0);
}

// Public

void DeviceManager::Present()
{
	mpSwapChain->Present(0,0);
}

void DeviceManager::Release()
{
	mpContext->ClearState();
	SAFE_RELEASE(mpContext);
	SAFE_RELEASE(mpRenderTargetView);
	SAFE_RELEASE(mpRenderTargetDepth);
	SAFE_RELEASE(mpSwapChain);
	SAFE_RELEASE(mpRasterState);
	SAFE_RELEASE(mpDepthState);
	SAFE_RELEASE(mpSamplerState);
	SAFE_RELEASE(mpDevice);
}

int DeviceManager::GetWidth()
{
	return mWidth;
}

int DeviceManager::GetHeight()
{
	return mHeight;
}

// Private

bool DeviceManager::CreateTargetView()
{
    ID3D11Texture2D *pBackBuffer;
    HR (mpSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (LPVOID*)&pBackBuffer ));
	HR(mpDevice->CreateRenderTargetView( pBackBuffer, NULL, &mpRenderTargetView ));
    pBackBuffer->Release();
    
	return true;
}

// Private

bool DeviceManager::CreateDepthStencilView(int width,int height)
{
	// crea depth stencil
	//create the depth stencil
    D3D11_TEXTURE2D_DESC descDepth;
    descDepth.Width = width;
    descDepth.Height = height;
    descDepth.MipLevels = 1;
    descDepth.ArraySize = 1;
	descDepth.Format = DXGI_FORMAT_D32_FLOAT;
    descDepth.SampleDesc.Count = 1;
    descDepth.SampleDesc.Quality = 0;
    descDepth.Usage = D3D11_USAGE_DEFAULT;
    descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    descDepth.CPUAccessFlags = 0;
    descDepth.MiscFlags = 0;
    
	ID3D11Texture2D *pDepthBuffer;

	HR(mpDevice->CreateTexture2D( &descDepth, NULL, &pDepthBuffer ));
   
    D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
	descDSV.Flags=0;
	descDSV.Format = DXGI_FORMAT_D32_FLOAT;
	descDSV.ViewDimension= D3D11_DSV_DIMENSION_TEXTURE2D;
	descDSV.Texture2D.MipSlice=0;
    HR(mpDevice->CreateDepthStencilView( pDepthBuffer, &descDSV, &mpRenderTargetDepth ));
    pDepthBuffer->Release();

	return true;
}